
/**
 * @file ajaxView.js
 *
 * Handles AJAX fetching of views, including filter submission and response.
 */

Drupal.Views.Ajax = Drupal.Views.Ajax || {};

/**
 * An ajax responder that accepts a packet of JSON data and acts appropriately.
 *
 * The following fields control behavior.
 * - 'display': Display the associated data in the view area.
 */
Drupal.Views.Ajax.ajaxViewResponse = function(target, response) {

    if (response.debug) {
        alert(response.debug);
    }

    var $view = $(target);

    // Check the 'display' for data.
    if (response.status && response.display) {
        var $newView = $(response.display);
        $view.replaceWith($newView);
        $view = $newView;
        Drupal.attachBehaviors($view.parent());
    }

    if (response.messages) {
        // Show any messages (but first remove old ones, if there are any).
        $view.find('.views-messages').remove().end().prepend(response.messages);
    }
};

/**
 * Ajax behavior for views.
 */
Drupal.behaviors.ViewsAjaxView = function() {
    if (Drupal.settings && Drupal.settings.views && Drupal.settings.views.ajaxViews) {
        var ajax_path = Drupal.settings.views.ajax_path;
        // If there are multiple views this might've ended up showing up multiple times.
        if (ajax_path.constructor.toString().indexOf("Array") != -1) {
            ajax_path = ajax_path[0];
        }
        $.each(Drupal.settings.views.ajaxViews, function(i, settings) {
            if (settings.view_dom_id) {
                var view = '.view-dom-id-' + settings.view_dom_id;
                if (!$(view).size()) {
                    // Backward compatibility: if 'views-view.tpl.php' is old and doesn't
                    // contain the 'view-dom-id-#' class, we fall back to the old way of
                    // locating the view:
                    view = '.view-id-' + settings.view_name + '.view-display-id-' + settings.view_display_id;
                }
            }


            // Process exposed filter forms.
            $('form#views-exposed-form-' + settings.view_name.replace(/_/g, '-') + '-' + settings.view_display_id.replace(/_/g, '-'))
                    .filter(':not(.views-processed)')
                    .each(function() {
                // remove 'q' from the form; it's there for clean URLs
                // so that it submits to the right place with regular submit
                // but this method is submitting elsewhere.
                $('input[name=q]', this).remove();
                var form = this;
                // ajaxSubmit doesn't accept a data argument, so we have to
                // pass additional fields this way.
                $.each(settings, function(key, setting) {
                    $(form).append('<input type="hidden" name="' + key + '" value="' + setting + '"/>');
                });
            })
                    .addClass('views-processed')
                    .submit(function() {
                $('input[type=submit], button', this).after('<span class="views-throbbing">&nbsp</span>');
                var object = this;
                $(this).ajaxSubmit({
                    url: ajax_path,
                    type: 'GET',
                    success: function(response) {
                        // Call all callbacks.
                        if (response.__callbacks) {
                            $.each(response.__callbacks, function(i, callback) {
                                eval(callback)(view, response);
                            });
                            $('.views-throbbing', object).remove();
                        }
                    },
                    error: function(xhr) {
                        Drupal.Views.Ajax.handleErrors(xhr, ajax_path);
                        $('.views-throbbing', object).remove();
                    },
                    dataType: 'json'
                });

                return false;
            });

            $(view).filter(':not(.views-processed)')
                    // Don't attach to nested views. Doing so would attach multiple behaviors
                    // to a given element.
                    .filter(function() {
                // If there is at least one parent with a view class, this view
                // is nested (e.g., an attachment). Bail.
                return !$(this).parents('.view').size();
            })
                    .each(function() {
                // Set a reference that will work in subsequent calls.
                var target = this;
                $(this)
                        .addClass('views-processed')
                        // Process pager, tablesort, and attachment summary links.
                        .find('ul.pager > li > a, th.views-field a, .attachment .views-summary a')
                        .each(function() {
                    var viewData = {'js': 1};
                    // Construct an object using the settings defaults and then overriding
                    // with data specific to the link.
                    $.extend(
                            viewData,
                            Drupal.Views.parseQueryString($(this).attr('href')),
                            // Extract argument data from the URL.
                            Drupal.Views.parseViewArgs($(this).attr('href'), settings.view_base_path),
                            // Settings must be used last to avoid sending url aliases to the server.
                            settings
                            );
                    $(this).click(function() {
                        $.extend(viewData, Drupal.Views.parseViewArgs($(this).attr('href'), settings.view_base_path));
                        $(this).addClass('views-throbbing');
                        $.ajax({
                            url: ajax_path,
                            type: 'GET',
                            data: viewData,
                            success: function(response) {
                                $(this).removeClass('views-throbbing');
                                // Scroll to the top of the view. This will allow users
                                // to browse newly loaded content after e.g. clicking a pager
                                // link.
                                var offset = $(target).offset();
                                // We can't guarantee that the scrollable object should be
                                // the body, as the view could be embedded in something
                                // more complex such as a modal popup. Recurse up the DOM
                                // and scroll the first element that has a non-zero top.
                                var scrollTarget = target;
                                while ($(scrollTarget).scrollTop() == 0 && $(scrollTarget).parent()) {
                                    scrollTarget = $(scrollTarget).parent()
                                }
                                // Only scroll upward
                                if (offset.top - 10 < $(scrollTarget).scrollTop()) {
                                    $(scrollTarget).animate({scrollTop: (offset.top - 10)}, 500);
                                }
                                // Call all callbacks.
                                if (response.__callbacks) {
                                    $.each(response.__callbacks, function(i, callback) {
                                        eval(callback)(target, response);
                                    });
                                }
                            },
                            error: function(xhr) {
                                $(this).removeClass('views-throbbing');
                                Drupal.Views.Ajax.handleErrors(xhr, ajax_path);
                            },
                            dataType: 'json'
                        });

                        return false;
                    });
                }); // .each function () {
            }); // $view.filter().each
        }); // .each Drupal.settings.views.ajaxViews
    } // if
};
